/*
 * @Author: pardon110
 * @Date: 2024-03-18 09:09:54
 * @LastEditors: pardon110@qq.com
 * @LastEditTime: 2024-03-18 10:40:20
 * @FilePath: \days7\web\middleware\mid\context.go
 * @Description:
 */
package mid

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type H map[string]interface{}

type Context struct {
	// origin objects
	Writer http.ResponseWriter
	Req    *http.Request
	// request info
	Path   string
	Method string
	Params map[string]string
	// response info
	StatusCode int

	// middleware
	handlers []HandlerFunc
	index    int // 维持中间件组内部状态计数
}

func newContext(w http.ResponseWriter, req *http.Request) *Context {
	return &Context{
		Path:   req.URL.Path,
		Method: req.Method,
		Req:    req,
		Writer: w,
		index:  -1, // 必须的，确保中间件内部调用c.next 时，先改变index状态，计数已走过的中间件
	}
}

// 调用 part1 -> part3 -> Handler -> part 4 -> part2
// 不是所有的handler都会调用 Next()
// 手工调用 Next()，一般用于在请求前后各实现一些行为。
// 若中间件只作用于请求前，可以省略调用Next()，算是一种兼容性比较好
func (c *Context) Next() {
	c.index++ // !!! 1.若无此句 index 初始值为0，则见2
	s := len(c.handlers)
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c) //2. 存在中间件内调用c.Next(),则index状态值不会变,间接自引用一直卡在第一个中间件
	}
}

func (c *Context) Fail(code int, err string) {
	c.index = len(c.handlers)
	c.JSON(code, H{"message": err})
}

func (c *Context) Param(key string) string {
	return c.Params[key]
}

func (c *Context) PostForm(key string) string {
	return c.Req.FormValue(key)
}

func (c *Context) Query(key string) string {
	return c.Req.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
	c.StatusCode = code
	c.Writer.WriteHeader(code)
}

func (c *Context) SetHeader(key string, value string) {
	c.Writer.Header().Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
	c.SetHeader("Content-Type", "text/plain")
	c.Status(code)
	c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
	c.SetHeader("Content-Type", "application/json")
	c.Status(code)
	encoder := json.NewEncoder(c.Writer)
	if err := encoder.Encode(obj); err != nil {
		http.Error(c.Writer, err.Error(), 500)
	}
}

func (c *Context) Data(code int, data []byte) {
	c.Status(code)
	c.Writer.Write(data)
}

func (c *Context) HTML(code int, html string) {
	c.SetHeader("Content-Type", "text/html")
	c.Status(code)
	c.Writer.Write([]byte(html))
}
